/**
 * 
 */
package com.jd.jdmvc.framework.template.context;

import java.util.*;
import javax.servlet.http.*;
import jetbrick.template.JetContext;

/**
 * 
 * @author liubing1
 *
 */
public class JdMvcContext extends JetContext {
    public static final String REQUEST = "request";
    public static final String RESPONSE = "response";
    public static final String PARAMETER = "parameter";
    public static final String PARAMETER_VALUES = "parameterValues";
    private final HttpServletRequest request;

    //@formatter:off
    private enum TYPE {
        REQUEST_SCOPE,
        PARAMETER, PARAMETER_VALUES,
        HEADER, HEADER_VALUES
    }
    //@formatter:on

    public JdMvcContext(HttpServletRequest request, HttpServletResponse response) {
        this(request, response, null);
    }

    public JdMvcContext(HttpServletRequest request, HttpServletResponse response, Map<String, Object> context) {
        super(context);

        this.request = request;
        put(REQUEST, request);
        put(RESPONSE, response);
        put(PARAMETER, TYPE.PARAMETER);
        put(PARAMETER_VALUES, TYPE.PARAMETER_VALUES);
    }

    @Override
    protected boolean isSimpleModel() {
        return false;
    }

    @Override
    public Object get(String name) {
        if (name == null) return null;

        Object value = super.get(name);
        if (value != null) {
            if (value instanceof TYPE) {
                value = createImplicitWebObject((TYPE) value);
                put(name, value); // resolved
            }
            return value;
        }
        value = request.getAttribute(name);
        if (value != null) {
            return value;
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    private Object createImplicitWebObject(TYPE type) {
        switch (type) {
        
        case PARAMETER: {
            return new EnumeratedMap<String, Object>() {
                @Override
                public Enumeration<String> enumerateKeys() {
                    return request.getParameterNames();
                }

                @Override
                public Object getValue(Object key) {
                    if (key instanceof String) {
                        return request.getParameter((String) key);
                    }
                    return null;
                }
            };
        }
        case PARAMETER_VALUES: {
            return new EnumeratedMap<String, Object>() {
                @Override
                public Enumeration<String> enumerateKeys() {
                    return request.getParameterNames();
                }

                @Override
                public Object getValue(Object key) {
                    if (key instanceof String) {
                        return request.getParameterValues((String) key);
                    }
                    return null;
                }
            };
        }
       
        default:
            return null;
        }
    }

    private static abstract class EnumeratedMap<K, V> implements Map<K, V> {
        private Map<K, V> map;

        public boolean containsKey(Object key) {
            return getValue(key) != null;
        }

        public boolean containsValue(Object value) {
            return getAsMap().containsValue(value);
        }

        public V get(Object key) {
            return getValue(key);
        }

        public boolean isEmpty() {
            return getAsMap().isEmpty();
        }

        public int size() {
            return getAsMap().size();
        }

        public Set<Map.Entry<K, V>> entrySet() {
            return getAsMap().entrySet();
        }

        public Set<K> keySet() {
            return getAsMap().keySet();
        }

        public Collection<V> values() {
            return getAsMap().values();
        }

        public V put(K key, V value) {
            throw new UnsupportedOperationException();
        }

        public void putAll(Map<? extends K, ? extends V> map) {
            throw new UnsupportedOperationException();
        }

        public V remove(Object key) {
            throw new UnsupportedOperationException();
        }

        public void clear() {
            throw new UnsupportedOperationException();
        }

        public abstract Enumeration<K> enumerateKeys();

        public abstract V getValue(Object key);

        public Map<K, V> getAsMap() {
            if (map == null) {
                synchronized (this) {
                    if (map == null) {
                        Map<K, V> result = new HashMap<K, V>();
                        for (Enumeration<K> e = enumerateKeys(); e.hasMoreElements();) {
                            K key = e.nextElement();
                            V value = getValue(key);
                            result.put(key, value);
                        }
                        map = result;
                    }
                }
            }
            return map;
        }
    }
}
